from flask import Flask, render_template, request, redirect, url_for, jsonify
import docker
import psutil  # 用于获取主机资源使用情况

app = Flask(__name__)
client = docker.from_env()


def check_ports_availability(ports):
    used_ports = get_used_ports()
    for port in ports.values():
        if port in used_ports:
            return False
    return True


def adjust_ports(ports):
    new_ports = {}
    for container_port, host_port in ports.items():
        while host_port in get_used_ports():  # 检测端口是否被占用
            host_port += 1  # 自动调整端口
        new_ports[container_port] = host_port
    return new_ports


def get_used_ports():
    used_ports = set()
    containers = client.containers.list()
    for container in containers:
        ports = container.attrs['HostConfig']['PortBindings']
        if ports:
            for port_info in ports.values():
                if port_info:
                    used_ports.add(int(port_info[0]['HostPort']))
    return used_ports


def get_host_memory_usage():
    memory = psutil.virtual_memory()  # 获取内存使用情况
    return memory.percent


def check_host_memory_availability():
    host_memory_usage = get_host_memory_usage()
    if host_memory_usage > 10:
        return False
    else:
        return True


def create_container(image, container_name, ports, memory=None, cpu_percentage=None):
    try:

        # 检查端口是否可用，如果不可用则自动调整端口
        while not check_ports_availability(ports):
            ports = adjust_ports(ports)

        # 检查内存使用率是否超过60%
        if not check_host_memory_availability():
            print("提示：当前节点内存资源不足，本地停止创建，已调度到其他节点创建！")
            # 本地停止创建
            # 调度到隔壁节点尝试运行
            # 如果隔壁节点不能运行，则跑回本地，继续循环等待。。
            # 如果循环踢皮球x次，则停止调度，任务创建失败。
            return 2

        # 将内存值从MB转换为B
        if memory is not None:
            memory = int(memory) * 1024 * 1024  # 将MB转换为B

        # 创建并启动容器
        container = client.containers.run(
            image,
            detach=True,
            tty=True,
            ports=ports,
            name=container_name,
            mem_limit=memory,  # 设置内存限制
            cpu_period=100000,  # 设置CPU周期
            cpu_quota=int(cpu_percentage * 10000),  # 设置CPU份额
        )
        print("容器创建并启动成功！")
        return container.id
    except docker.errors.APIError as e:
        print(f"出现错误：{e}")
        return None


# 获取容器列表
@app.route('/containers')
def list_containers():
    containers = client.containers.list(all=True)  # 获取所有容器，包括Exited和Created状态的容器
    containers_data = []
    for container in containers:
        container_data = {
            'id': container.id,
            'name': container.name,
            'status': container.status,
            'cpu_usage_percentage': None,
            'memory_usage': container.attrs['HostConfig']['Memory'] / (1024 * 1024),  # 将内存从B转换为MB
            'memory_limit': container.attrs['HostConfig']['Memory'] / (1024 * 1024),  # 将内存从B转换为MB
            'ports': container.attrs['NetworkSettings']['Ports']
        }
        if container.status == 'running':
            # 仅当容器处于运行中状态时获取CPU使用率信息
            try:
                cpu_usage_percentage = container.stats(stream=False)['cpu_stats']['cpu_usage']['total_usage'] / container.stats(stream=False)['cpu_stats']['system_cpu_usage'] * 100
                container_data['cpu_usage_percentage'] = cpu_usage_percentage
            except KeyError:
                # 如果不存在系统 CPU 使用率信息，则置为 None
                container_data['cpu_usage_percentage'] = None
        containers_data.append(container_data)
    return jsonify(containers_data)



# 创建容器
@app.route('/containers/create', methods=['POST'])
def create_container_route():
    image = request.form.get('image', 'ubuntu')
    container_name = request.form.get('container_name', 'my_container')
    ports_str = request.form.get('ports', None)
    memory = request.form.get('memory', None)  # 获取内存值
    cpu_percentage = float(request.form.get('cpu_percentage', 50))  # 获取CPU使用率

    # 确保内存值不低于6MB
    if memory is not None and int(memory) < 6:
        print("提示：内存限制不能低于6MB")
        return redirect(url_for('index'))  # 返回到主页

    ports_dict = {}
    if ports_str:
        # 将端口字符串解析为字典
        ports_list = ports_str.split(',')
        for port_pair in ports_list:
            host_port, container_port = port_pair.split(':')
            ports_dict[f'{container_port}/tcp'] = int(host_port)

    # 创建并启动新容器
    create_container(image, container_name, ports_dict, memory, cpu_percentage)

    return redirect(url_for('index'))


# 获取主机资源
@app.route('/host_resources')
def host_resources():
    memory = psutil.virtual_memory()
    return jsonify({
        'cpu_percent': psutil.cpu_percent(),
        'memory_percent': memory.percent,
        'memory_total': memory.total,
        'memory_used': memory.used
    })


# 启动容器
@app.route('/containers/<container_id>/start', methods=['POST'])
def start_container(container_id):
    container = client.containers.get(container_id)
    container.start()
    return '', 204


# 重启容器
@app.route('/containers/<container_id>/stop_and_restart', methods=['POST'])
def stop_and_restart_container(container_id):
    container = client.containers.get(container_id)
    container.restart()
    return '', 204


# 停止容器
@app.route('/containers/<container_id>/stop', methods=['POST'])
def stop_container(container_id):
    container = client.containers.get(container_id)
    container.stop()
    return '', 204


# 删除容器
@app.route('/containers/<container_id>/delete', methods=['POST'])
def delete_container(container_id):
    container = client.containers.get(container_id)
    container.remove(force=True)
    return '', 204


# 主页
@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':
    app.run(debug=True)
